#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "lz/lz78.h"
#include "lz/lz77.h"
#include "huffman/huffman.h"
#include "pretraitement/bwt.h"

#define ALGO_huffman	"huff"
#define ALGO_LZ77		"lz77"
#define ALGO_LZ78		"lz78"
#define ALGO_DEFL		"defl"
#define ALGO_BZIP		"bzip"



typedef struct {
	char * inputFile;
	char * outputFile;
	char compression;
	char algo[5];
        int verbose;
} options_t;

char prefix[12] = "plopplop";

//-----------------------------------------------------------------------------
void printUsage()
//-----------------------------------------------------------------------------
{
	printf("Usage : %s <inputFile> <outputFile> ACTION [ALGORITHME]\n",prefix);
	printf("ACTION :\n \
	-c\tCompression\n\
	-d\tDécompression\n\
        -v\tVerbose mode\n\
\n\
ALGORITHME :\n \
	-t algo\n\
		Type d'algo utilisé. Cette option est utile uniquement lorsque l'option\n\
		-c est activée. La valeur algo peut prendre les valeurs suivantes :\n\
			huff :  Algorithme de huffman.\n\
			lz77 :  LZ77.\n\
			lz78 :  LZ78.\n\
			bzip :  BZIP.\n\
			defl :  Algiorithme Deflate/Inflate (LZ77+Huffman)\n\
	\n");
}

//-----------------------------------------------------------------------------
void readopts(options_t * opts, int argc, char ** argv)
//-----------------------------------------------------------------------------
//bidule <input_file> [ <output_file> [-cd -t algo]]
{
	int i;

	opts->inputFile = argv[1];//strdup(argv[1]);
	opts->outputFile = argv[2];//strdup(argv[2]);
	opts->compression = '\0';
	opts->verbose = 0;
	strcpy(opts->algo,"lz77");

	for(i = 3; i < argc; i++)
	{
		if( argv[i][0] == '-' )
		{
			switch( argv[i][1] )
			{
			case 'c':
				//compression
				if(!opts->compression) opts->compression = 'c';
				else if (opts->compression == 'd')
				{
					printf("%s: les arguments -c et -d sont incompatibles\n",prefix);
					exit(EXIT_FAILURE);
				}else if (opts->compression=='p') {opts->compression = 'q';} //prétraitement + compression

				break;
			case 'd':
				//decompression
				if(!opts->compression) opts->compression = 'd';
				else if (opts->compression == 'c')
				{
					printf("%s: les arguments -c et -d sont incompatibles\n",prefix);
					exit(EXIT_FAILURE);
				}else if (opts->compression=='p') {opts->compression = 's';} //décompression + déprétraitement
				break;
			case 'p':
				//prétraitement
				if (!opts->compression) opts->compression = 'p';
				else if (opts->compression=='c'){opts->compression = 'q';} //pretraitement + compression
				else if (opts -> compression=='d') {opts->compression = 's';} //pretraitement + decompression
				break;
			case 'r':
				//reverse pretraitement
				if (!opts->compression) opts->compression = 'r';
				else if (opts->compression=='c'){printf("impossible de compresser et désencoder le prétraitement en même temps");exit(0);} //pretraitement + compression
				else if (opts -> compression=='d') {opts->compression = 's';} //pretraitement + decompression
				break;
			case 't':
				//type de compression {huff, lz77, lz78, ...}
				strncpy(opts->algo,argv[++i],5);
				break;
			case 'v' :
			        //On active le verbose mode
			        opts->verbose = 1;
				break;
			default:
				printf("%s: argument inconnu (%s)\n", prefix, argv[i]);
				break;

			};
		} else
		{
			printf(" %s : argument invalide\n", argv[i]);
			exit(EXIT_FAILURE);
		}
	}

	if(!opts->compression){
		printUsage();
		exit(EXIT_FAILURE);
	}
}

//-----------------------------------------------------------------------------
int main(int argc, char ** argv)
//-----------------------------------------------------------------------------
{
	options_t opts;
	FILE * fin;
	FILE * fout;
	FILE * ftemp=NULL;
	char	id[5];
	id[4]='\0';

	strncpy(prefix, argv[0], 12);

	readopts(&opts, argc, argv);


	if(!(fin =fopen(opts.inputFile,"rb"))){
		printf("%s: fopen(): %s (%s)\n",prefix,strerror(errno),opts.inputFile);
		return errno;
	}

	//On regarde si le fichier à coder ou decoder est vide
	fseek(fin,0,SEEK_END);
	int fileLen = ftell(fin);
	rewind(fin);
	if(fileLen == 0)
		{
			printf("Le fichier %s est vide\n",argv[1]);
			exit(EXIT_SUCCESS);
		}

	if(!(fout=fopen(opts.outputFile,"w+b"))){
		printf("%s: fopen(): %s (%s)\n",prefix,strerror(errno),opts.outputFile);
		return errno;
	}

	//printf("inputfile = %s\noutputfile = %s\n",opts.inputFile, opts.outputFile);

	if(opts.compression == 'c')
	{
		//CHOIX DE L'ALGO DE COMPRESSION

	  //TODO : rajouter le vervose mode pour huffman
		if(!strncmp(ALGO_huffman,opts.algo,5)){
			printf("Compression Huffman GOGOGO\n");
			HUFF_compress(fout,fin,HUFFMANID);

		}else if(!strncmp(ALGO_LZ77,opts.algo,5)){
			printf("Compression LZ77 GOGOGO !\n");
			LZ77_compress(fout,fin,opts.verbose);

		}else if(!strncmp(ALGO_LZ78,opts.algo,5)){
			printf("Compression LZ78 GOGOGO !\n");
			LZ78_compress(fout,fin,opts.verbose);

		}else if(!strncmp(ALGO_DEFL,opts.algo,5)){
			printf("Compression DEFLATE GOGOGO !\n");
			ftemp=tmpfile();	//création d'un fichier temporaire
			printf("LZ78 ...\n");
			LZ78_compress(ftemp,fin,opts.verbose);
			rewind(ftemp);
			printf("Huffman ...\n");
			HUFF_compress(fout,ftemp,DEFLATEID);
			printf(":D");
			fclose(ftemp);

		}else if(!strncmp(ALGO_BZIP,opts.algo,5)){
			printf("Compression BZIP GOGOGO !\n");
			ftemp=tmpfile();	//création d'un fichier temporaire
			printf("Transformée de Burrow&Wheller...\n");
			BWT_fileEncode(ftemp,fin);
			rewind(ftemp);
			printf("Compression Huffman ...\n");
			HUFF_compress(fout,ftemp,BZIPID);
			printf(":D");
			fclose(ftemp);

		}else{
			printf("%s: Algorithme inconnu (%s)\n",prefix,opts.algo);
			return EXIT_FAILURE;
		}
	}else if(opts.compression == 'd'){
		// DECOMPRESSION
		printf("décompression ");
		fread(id, 4, 1, fin);
		if(ferror(fin))
		{
			printf("%s: fread(): %s (%s)\n",prefix,strerror(errno),opts.inputFile);
			return errno;
		}
		rewind(fin);
		if(!strncmp(id,LZ78ID,4))
		{
			printf("Décompression LZ78 GOGOGO !\n");
			LZ78_decompress(fout,fin);

		}else if( !strncmp(id, LZ77ID, 4) ){
			printf("Décompression LZ77 GOGOGO !\n");
			LZ77_decompress(fout,fin,opts.verbose);

		}else if( !strncmp(id, HUFFMANID, 4) ){
			printf("Décompression HUFFMAN GOGOGO !\n");
			HUFF_decompress(fout,fin);

		}else if( !strncmp(id, DEFLATEID, 4) ){
			printf("Décompression DEFLATE GOGOGO !\n");
			ftemp=tmpfile();	//création d'un fichier temporaire
			printf("Huffman...\n");
			HUFF_decompress(ftemp,fin);
			rewind(ftemp);
			printf("LZ78 ...\n");
			LZ78_decompress(fout,ftemp);
			fclose(ftemp);

		}else if( !strncmp(id, BZIPID, 4) ){
			printf("Décompression BZIP GOGOGO !\n");
			ftemp=tmpfile();	//création d'un fichier temporaire
			printf("Décompression Huffman...\n");
			HUFF_decompress(ftemp,fin);
			rewind(ftemp);
			BWT_fileDecode(fout,ftemp);
			printf("Transformée inverse de Burrow&Wheller...\n");
			fclose(ftemp);

		}else{
			printf("%s: %s type de fichier inconnu\n",prefix, opts.inputFile);
			return EXIT_FAILURE;
		}

	}else if (opts.compression == 'p'){
		// PRETRAITEMENT
		printf("Transformée de Burrow&Wheller: GOGOGOGOGO \n");
		BWT_fileEncode(fout,fin);

	}else if (opts.compression =='r'){
		// REVERSE PRETRAITEMENT
		printf("Transformée inverse de Burrow&Wheller GOGOGO\n");
		BWT_fileDecode(fout,fin);


	}

	//free(opts.inputFile);
	//free(opts.outputFile);
	fclose(fin);
	fclose(fout);

	return EXIT_SUCCESS;
}

//
